import uuid
from datetime import datetime, timedelta
from flask import Flask, request, jsonify, render_template, redirect, send_file
import pandas as pd
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, String, DateTime
from enum import IntEnum
import os

from utils.base_util import decrypt_id,encrypt_id
from utils.pdf_util import generate_pdf

from utils.dateUtil import date_to_str
import asyncio
from concurrent.futures import ThreadPoolExecutor
import threading
from utils.OssUtil import bucket
import zipfile
import io
import tempfile

app = Flask(__name__)



app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


db = SQLAlchemy(app)

migrate = Migrate(app, db)


# 自动重载模板文件
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
# 设置静态文件缓存过期时间
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1)

class PdfStatus(IntEnum):
    """PDF 生成状态枚举
    
    Attributes:
        PENDING (0): 等待处理
        COMPLETED (1): 处理完成
    """
    PENDING = 0
    COMPLETED = 1

    @classmethod
    def get_description(cls, status):
        """获取状态描述"""
        descriptions = {
            cls.PENDING: '等待处理',
            cls.COMPLETED: '处理完成'
        }
        return descriptions.get(status, '未知状态')

# 定义数据模型
class PdfData(db.Model):

    __tablename__ = 'pdf_data'
    
    id = Column(Integer, primary_key=True)
    create_time = Column(DateTime, default=datetime.now)
    pdf_file_0 = Column(String)
    pdf_file_1 = Column(String)
    pdf_status = Column(Integer, default=PdfStatus.PENDING)
    
    notify_name = Column(String, comment='通知人')
    company = Column(String, comment='公司')
    deadline = Column(String, comment='截至日期')
    office = Column(String, comment='事务所')
    date = Column(String, comment='日期')
    phone = Column(String, comment='电话')
    lawyer = Column(String, comment='律师')

    # 类属性用于存储字段映射关系
    column_map = {
        '通知人': 'notify_name',
        '公司': 'company',
        '截至日期': 'deadline',
        '事务所': 'office',
        '日期': 'date',
        '电话': 'phone',
        '律师': 'lawyer',
    }

    @classmethod
    def get_column_map(cls):
        """获取字段映射关系"""
        return cls.column_map

    @classmethod
    def get_reverse_column_map(cls):
        """获取反向字段映射关系"""
        return {v: k for k, v in cls.column_map.items()}

# 创建一个线程池用于处理 CPU 密集型的 PDF 生成任务
pdf_executor = ThreadPoolExecutor(max_workers=1)

# 添加全局锁
pdf_processing_lock = threading.Lock()

def process_pdf_task(pending_record):
    """处理单个 PDF 生成任务"""
    try:
        # 准备数据
        data = {
            k: getattr(pending_record, v)
            for k, v in PdfData.get_column_map().items()
        }

        key = uuid.uuid4()

        pdf_file_0 = generate_pdf(data, 0,key)
        pdf_file_1 = generate_pdf(data, 1,key)

        # 更新记录
        pending_record.pdf_file_0 = pdf_file_0
        pending_record.pdf_file_1 = pdf_file_1
        pending_record.pdf_status = PdfStatus.COMPLETED
        db.session.commit()

    except Exception as e:
        print(f"Error processing PDF for row {pending_record.id}: {str(e)}")
        db.session.rollback()

async def process_pending_pdfs():
    """异步处理 PDF 队列"""
    while True:

        try:
            with app.app_context():
                # 获取待处理记录
                pending_record = PdfData.query.filter_by(
                    pdf_status=PdfStatus.PENDING
                ).first()

                if pending_record:
                    # 一次处理一个任务
                    print('开始处理任务')
                    process_pdf_task(pending_record)

                # 无论是否有任务都稍微暂停一下，避免频繁查询数据库
                await asyncio.sleep(2)

        except Exception as e:
            print(f"Error in process_pending_pdfs: {str(e)}")
            # 发生错误时暂停较长时间后继续
            await asyncio.sleep(5)
            continue


@app.route('/')
def index():
    return render_template('index.html')

# 替换 get_pdf_data 路由
@app.route('/pdf_data', methods=['GET'])
def get_pdf_data():
    page = int(request.args.get('page', 1))
    per_page = int(request.args.get('per_page', 10))

    pagination = PdfData.query.order_by(
        PdfData.create_time.desc()
    ).paginate(page=page, per_page=per_page)

    data = [{
        **item.__dict__,
        'create_time': item.create_time.strftime('%Y-%m-%d %H:%M:%S'),
        '_sa_instance_state': None  # 移除 SQLAlchemy 内部状态
    } for item in pagination.items]

    return jsonify({
        'data': data,
        'total': pagination.total,
    })

# 替换 upload_excel 路由
@app.route('/upload_excel', methods=['POST'])
def upload_excel():
    file = request.files['file']
    if not file:
        return jsonify({'error': 'No file uploaded'}), 400

    df = pd.read_excel(file)

    df['截至日期'] = df['截至日期'].apply(date_to_str)
    df['日期'] = df['日期'].apply(date_to_str)

    for _, row in df.iterrows():
        new_record = PdfData(
            pdf_status=PdfStatus.PENDING,
            **{PdfData.get_column_map()[col]: row[col] 
               for col in PdfData.get_column_map().keys()}
        )
        db.session.add(new_record)
    
    db.session.commit()
    return jsonify({'message': 'Data uploaded successfully'})



# 添加在其他路由之前
@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    
    # 硬编码的用户列表
    valid_users = [
        {'username': 'admin', 'password': '2025qqbc'},
        {'username': 'user', 'password': '2025qqbc'}
    ]
    
    if any(user['username'] == username and user['password'] == password for user in valid_users):
        return jsonify({'success': True})
    
    return jsonify({'success': False, 'message': '用户名或密码错误'}), 401

def run_async_tasks():
    """在独立线程中运行异步任务"""
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(process_pending_pdfs())

@app.route('/batch_delete', methods=['POST'])
def batch_delete():
    try:
        data = request.get_json()
        ids = data.get('ids', [])
        
        if not ids:
            return jsonify({'success': False, 'message': 'No IDs provided'}), 400
            
        # Get the records to be deleted
        records = PdfData.query.filter(PdfData.id.in_(ids)).all()
        
        # Delete OSS files
        for record in records:
            if record.pdf_file_0:
                try:
                    bucket.delete_object(record.pdf_file_0)
                except Exception as e:
                    print(f"Error deleting OSS file {record.pdf_file_0}: {str(e)}")
            if record.pdf_file_1:
                try:
                    bucket.delete_object(record.pdf_file_1)
                except Exception as e:
                    print(f"Error deleting OSS file {record.pdf_file_1}: {str(e)}")
        
        # Delete database records
        PdfData.query.filter(PdfData.id.in_(ids)).delete()
        db.session.commit()
        
        return jsonify({'success': True, 'message': 'Records deleted successfully'})
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'success': False, 'message': str(e)}), 500

@app.route('/download_template')
def download_template():
    try:
        return send_file(
            'files/template/导入模板.xlsx',
            as_attachment=True,
            download_name='template.xlsx'
        )
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)}), 500

@app.route('/batch_download', methods=['POST'])
def batch_download():
    try:
        data = request.get_json()
        ids = data.get('ids', [])
        
        if not ids:
            return jsonify({'success': False, 'message': 'No IDs provided'}), 400
            
        # Get the records
        records = PdfData.query.filter(PdfData.id.in_(ids)).all()
        
        # Create a temporary file to store the zip
        temp_zip = io.BytesIO()
        with zipfile.ZipFile(temp_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for record in records:
                if record.pdf_file_0:
                    try:
                        # Download file from OSS
                        obj = bucket.get_object(record.pdf_file_0)
                        # Add to zip with a meaningful name
                        zipf.writestr(f'资方_{record.notify_name}_{record.company}.pdf', obj.read())
                    except Exception as e:
                        print(f"Error downloading file {record.pdf_file_0}: {str(e)}")
                
                if record.pdf_file_1:
                    try:
                        # Download file from OSS
                        obj = bucket.get_object(record.pdf_file_1)
                        # Add to zip with a meaningful name
                        zipf.writestr(f'自营_{record.notify_name}_{record.company}.pdf', obj.read())
                    except Exception as e:
                        print(f"Error downloading file {record.pdf_file_1}: {str(e)}")
        
        # Prepare the response
        temp_zip.seek(0)
        return send_file(
            temp_zip,
            mimetype='application/zip',
            as_attachment=True,
            download_name='批量下载.zip'
        )
        
    except Exception as e:
        return jsonify({'success': False, 'message': str(e)}), 500

if __name__ == '__main__':
    # 在独立线程中启动 PDF 处理协程
    pdf_thread = threading.Thread(target=run_async_tasks, daemon=True)
    pdf_thread.start()

    # 启动 Flask 应用
    # 局域网访问和80端口
    app.run(host='0.0.0.0', port=80,debug=True, use_reloader=True, threaded=True)
